/**
 * Created by IntelliJ IDEA.
 * User: Tomek
 * Date: 11.10.12
 * Time: 23:36
 * To change this template use File | Settings | File Templates.
 */
package fossilgeom{
import flash.geom.Point;
import flash.display.Graphics;


public class GeomUtils {
    public function GeomUtils() {
    }

    public static function getLineIntersection (
            a1:Point,a2:Point,
            b1:Point,b2:Point
            ):Point {

        //calculate directional constants
        var k1:Number = (a2.y-a1.y) / (a2.x-a1.x);
        var k2:Number = (b2.y-b1.y) / (b2.x-b1.x);

        // if the directional constants are equal, the lines are parallel,
        // meaning there is no intersection point.
        if( k1 == k2 ) return null;

        var x:Number,y:Number;
        var m1:Number,m2:Number;

        // an infinite directional constant means the line is vertical
        if( !isFinite(k1) ) {

            // so the intersection must be at the x coordinate of the line
            x = a1.x;
            m2 = b1.y - k2 * b1.x;
            y = k2 * x + m2;

            // same as above for line 2
        } else if ( !isFinite(k2) ) {

            m1 = a1.y - k1 * a1.x;
            x = b1.x;
            y = k1 * x + m1;

            // if neither of the lines are vertical
        } else {

            m1 = a1.y - k1 * a1.x;
            m2 = b1.y - k2 * b1.x;
            x = (m1-m2) / (k2-k1);
            y = k1 * x + m1;

        }

        return new Point(x,y);
    }

    public static function drawCurveArrow(myGraphics:Graphics,
                                          start:Point,end:Point,curve:Point,
                                          style:Object=null):void {

        if (start.equals(end)) return;

        var arrowStyle:ArrowStyle;
        if (style == null) {
            arrowStyle = new ArrowStyle();
        } else if (style is ArrowStyle) {
            arrowStyle = style as ArrowStyle;
        } else {
            arrowStyle = new ArrowStyle(style);
        }

        // if arrow will be bigger than line,
        // just draw the line
        var testDist:Point = end.subtract(start);
        if (testDist.length<arrowStyle.headLength)
        {
            myGraphics.lineStyle(arrowStyle.shaftThickness);
            myGraphics.moveTo(start.x,start.y);
            myGraphics.lineTo(end.x,end.y);
            return;
        }

        var fullVect:Point = end.subtract(start);
        var halfWidth:Number = (arrowStyle.headWidth != -1) ? arrowStyle.headWidth/2 : arrowStyle.headLength/2;

        //Figure out the line start/end points
        var startNorm:Point = new Point(fullVect.y,-fullVect.x);
        startNorm.normalize(arrowStyle.shaftThickness/2);
        var start1:Point = start.add(startNorm);
        var start2:Point = start.subtract(startNorm);
        var curve1:Point = curve.add(startNorm);
        var curve2:Point = curve.subtract(startNorm);

        var endFullVect:Point = end.subtract(curve);
        var endNorm:Point = new Point(endFullVect.y,-endFullVect.x);
        endNorm.normalize(arrowStyle.shaftThickness/2);
        var end1:Point = end.add(endNorm);
        var end2:Point = end.subtract(endNorm);

        //figure out where the arrow head starts
        var headPnt:Point = endFullVect.clone();
        headPnt.normalize(headPnt.length-arrowStyle.headLength);
        headPnt = headPnt.add(curve);

        //calculate the arrowhead corners
        var headPntNorm:Point = endNorm.clone();
        headPntNorm.normalize(halfWidth);
        var edge1:Point = headPnt.add(headPntNorm);
        var edge2:Point = headPnt.subtract(headPntNorm);

        //Figure out where the arrow connects the the shaft, then calc the intersections
        var shaftCenter:Point = Point.interpolate(end,headPnt,arrowStyle.shaftPosition);
        var inter1:Point = getLineIntersection(curve1,end1,shaftCenter,edge1);
        var inter2:Point = getLineIntersection(curve2,end2,shaftCenter,edge2);

        //Figure out the control points
        var edgeCenter:Point = Point.interpolate(end,headPnt,arrowStyle.edgeControlPosition);
        var edgeNorm:Point = endNorm.clone();
        edgeNorm.normalize(halfWidth*arrowStyle.edgeControlSize);
        var edgeCntrl1:Point = edgeCenter.add(edgeNorm);
        var edgeCntrl2:Point = edgeCenter.subtract(edgeNorm);


        myGraphics.moveTo(start1.x,start1.y);
        //fossilgeom.lineTo(inter1.x,inter1.y);
        myGraphics.curveTo(curve1.x,curve1.y,inter1.x,inter1.y);
        myGraphics.lineTo(edge1.x,edge1.y);
        myGraphics.curveTo(edgeCntrl1.x,edgeCntrl1.y,end.x,end.y);
        myGraphics.curveTo(edgeCntrl2.x,edgeCntrl2.y,edge2.x,edge2.y);
        myGraphics.lineTo(inter2.x,inter2.y);
        //fossilgeom.lineTo(start2.x,start2.y);
        myGraphics.curveTo(curve2.x,curve2.y,start2.x,start2.y);
        myGraphics.lineTo(start1.x,start1.y);
    }

    public static function drawArrow(graphics:Graphics,
                                     start:Point,end:Point,
                                     style:Object=null):void {

        if (start.equals(end)) return;

        var arrowStyle:ArrowStyle;
        if (style == null) {
            arrowStyle = new ArrowStyle();
        } else if (style is ArrowStyle) {
            arrowStyle = style as ArrowStyle;
        } else {
            arrowStyle = new ArrowStyle(style);
        }

        var fullVect:Point = end.subtract(start);
        var halfWidth:Number = (arrowStyle.headWidth != -1) ? arrowStyle.headWidth/2 : arrowStyle.headLength/2;

        //Figure out the line start/end points
        var startNorm:Point = new Point(fullVect.y,-fullVect.x);
        startNorm.normalize(arrowStyle.shaftThickness/2);
        var start1:Point = start.add(startNorm);
        var start2:Point = start.subtract(startNorm);
        var end1:Point = end.add(startNorm);
        var end2:Point = end.subtract(startNorm);

        //figure out where the arrow head starts
        var headPnt:Point = fullVect.clone();
        headPnt.normalize(headPnt.length-arrowStyle.headLength);
        headPnt = headPnt.add(start);

        //calculate the arrowhead corners
        var headPntNorm:Point = startNorm.clone();
        headPntNorm.normalize(halfWidth);
        var edge1:Point = headPnt.add(headPntNorm);
        var edge2:Point = headPnt.subtract(headPntNorm);

        //Figure out where the arrow connects the the shaft, then calc the intersections
        var shaftCenter:Point = Point.interpolate(end,headPnt,arrowStyle.shaftPosition);
        var inter1:Point = getLineIntersection(start1,end1,shaftCenter,edge1);
        var inter2:Point = getLineIntersection(start2,end2,shaftCenter,edge2);

        //Figure out the control points
        var edgeCenter:Point = Point.interpolate(end,headPnt,arrowStyle.edgeControlPosition);
        var edgeNorm:Point = startNorm.clone();
        edgeNorm.normalize(halfWidth*arrowStyle.edgeControlSize);
        var edgeCntrl1:Point = edgeCenter.add(edgeNorm);
        var edgeCntrl2:Point = edgeCenter.subtract(edgeNorm);


        graphics.moveTo(start1.x,start1.y);
        graphics.lineTo(inter1.x,inter1.y);
        graphics.lineTo(edge1.x,edge1.y);
        graphics.curveTo(edgeCntrl1.x,edgeCntrl1.y,end.x,end.y);
        graphics.curveTo(edgeCntrl2.x,edgeCntrl2.y,edge2.x,edge2.y);
        graphics.lineTo(inter2.x,inter2.y);
        graphics.lineTo(start2.x,start2.y);
        graphics.lineTo(start1.x,start1.y);
    }

    public static function drawArrowHead(graphics:Graphics,
                                         start:Point,end:Point,
                                         style:Object=null):void {

        var arrowStyle:ArrowStyle;
        if (style == null) {
            arrowStyle = new ArrowStyle();
        } else if (style is ArrowStyle) {
            arrowStyle = style as ArrowStyle;
        } else {
            arrowStyle = new ArrowStyle(style);
        }

        var vec:Point = end.subtract(start);
        var halfWidth:Number = (arrowStyle.headWidth != -1) ? arrowStyle.headWidth/2 : vec.length/2;

        var shaft:Point = vec.clone();
        shaft.normalize(vec.length*arrowStyle.shaftPosition);
        shaft = start.add(shaft);

        //Make the vect a normal to the line
        vec = new Point(vec.y,-vec.x);

        var tmp:Point = vec.clone();
        tmp.normalize(arrowStyle.shaftControlSize*halfWidth);
        var shaftCenter:Point = Point.interpolate(start,shaft,arrowStyle.shaftControlPosition);
        var baseCntrl1:Point = shaftCenter.add(tmp);
        var baseCntrl2:Point = shaftCenter.subtract(tmp);

        vec.normalize(halfWidth);
        var base1:Point = start.add(vec);
        var base2:Point = start.subtract(vec);

        var edgeCenter:Point = Point.interpolate(start,end,arrowStyle.edgeControlPosition);
        vec.normalize(halfWidth*arrowStyle.edgeControlSize);
        var edgeCntrl1:Point = edgeCenter.add(vec);
        var edgeCntrl2:Point = edgeCenter.subtract(vec);


        //Draw the arrow
        graphics.moveTo(base1.x,base1.y);
        graphics.curveTo(baseCntrl1.x,baseCntrl1.y,shaft.x,shaft.y);
        graphics.curveTo(baseCntrl2.x,baseCntrl2.y,base2.x,base2.y);
        graphics.curveTo(edgeCntrl2.x,edgeCntrl2.y,end.x,end.y);
        graphics.curveTo(edgeCntrl1.x,edgeCntrl1.y,base1.x,base1.y);

    }

    private static function inRange(pnt:Point,a:Point,b:Point):Boolean {

        if (a.x != b.x) {
            return pnt.x <= a.x != pnt.x < b.x;
        } else {
            return pnt.y <= a.y != pnt.y < b.y;
        }

    }
}
}
